package com.yscp.ysdemo.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yscp.ysdemo.common.WordsFilter;
import com.yscp.ysdemo.constant.Constants;
import com.yscp.ysdemo.constant.MqConstants;
import com.yscp.ysdemo.constant.RedisConstant;
import com.yscp.ysdemo.constant.ResultEnum;
import com.yscp.ysdemo.dto.LikeDto;
import com.yscp.ysdemo.dto.ResponseResult;
import com.yscp.ysdemo.dto.UserDetail;
import com.yscp.ysdemo.entity.*;
import com.yscp.ysdemo.exception.BusinessException;
import com.yscp.ysdemo.mapper.LikeMapper;
import com.yscp.ysdemo.mapper.UserConfigMapper;
import com.yscp.ysdemo.mapper.UserExtendMapper;
import com.yscp.ysdemo.service.UserService;
import com.yscp.ysdemo.mapper.UserMapper;
import com.yscp.ysdemo.utils.UserHolder;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
* @author Administrator
* @description 针对表【user】的数据库操作Service实现
* @createDate 2023-02-20 08:39:02
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
    implements UserService{


    @Resource
    private WordsFilter wordsFilter;

    @Resource
    private UserConfigMapper userConfigMapper;

    @Resource
    private UserMapper userMapper;

    @Resource
    private UserExtendMapper userExtendMapper;

    @Resource
    private LikeMapper likeMapper;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private RabbitTemplate rabbitTemplate;

    //改变点赞状态的lua脚本,用Integer会报错，得用long
    private static  final DefaultRedisScript<Long> LIKE_SCRIPT;
    static {
        LIKE_SCRIPT = new DefaultRedisScript<>();
        LIKE_SCRIPT.setLocation(new ClassPathResource("lua/updateLike.lua"));
        LIKE_SCRIPT.setResultType(Long.class);
    }

    //改变关注状态的lua脚本
    private static  final DefaultRedisScript<Long> FOCUS_SCRIPT;
    static {
        FOCUS_SCRIPT = new DefaultRedisScript<>();
        FOCUS_SCRIPT.setLocation(new ClassPathResource("lua/updateFocus.lua"));
        FOCUS_SCRIPT.setResultType(Long.class);
    }


    //改变关注状态的lua脚本
    private static  final DefaultRedisScript<Long> COLLECT_SCRIPT;
    static {
        COLLECT_SCRIPT = new DefaultRedisScript<>();
        COLLECT_SCRIPT.setLocation(new ClassPathResource("lua/updateCollect.lua"));
        COLLECT_SCRIPT.setResultType(Long.class);
    }
    /**
     * 保存用户信息
     * @param config
     * @param configType
     * @return
     */
    @Override
    public ResponseResult savePersonalConfig(String config, Integer configType) {
        User user = UserHolder.getUser();
        UserConfig userConfig = new UserConfig();
        userConfig.setUserId(user.getUserId());
        userConfig.setConfigType(configType);
        userConfig.setAction(config);
        LambdaQueryWrapper<UserConfig>  userConfigLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userConfigLambdaQueryWrapper.eq(UserConfig::getUserId,user.getUserId()).eq(UserConfig::getConfigType,configType);
        UserConfig oldUserConfig = userConfigMapper.selectOne(userConfigLambdaQueryWrapper);
        if (oldUserConfig == null){
            userConfigMapper.insert(userConfig);
        }else{
            userConfigMapper.updateById(userConfig);
        }
        return ResponseResult.success("保存成功");
    }

    /**
     * 查找用户信息
     * @param hostId
     * @param configType
     * @return
     */
    @Override
    public ResponseResult getPersonalConfig(Integer hostId, Integer configType) {
        LambdaQueryWrapper<UserConfig> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(UserConfig::getUserId,hostId).eq(UserConfig::getConfigType,configType);
        UserConfig userConfig = userConfigMapper.selectOne(lambdaQueryWrapper);
        if (userConfig == null)
            return ResponseResult.success(JSON.toJSONString(Constants.DEFAULT_PERSONALCENTER));
        return ResponseResult.success(userConfig.getAction());
    }

    /**
     * 保存用户更新信息，对每个可能的传参校验,更新字段有限，最好细粒度更新
     * @param userDetail
     * @return
     */
    @Override
    @Transactional
    public ResponseResult updateUserMsg(UserDetail userDetail) {
        if (StringUtils.isNotEmpty(userDetail.getUsername()) && StringUtils.isBlank(userDetail.getUsername())){
            throw new BusinessException(ResultEnum.PARAM_ERROR);
        }
        if (wordsFilter.checkWord(userDetail.getUsername())){
            throw new BusinessException(ResultEnum.SENSITIVE_WORDS);
        }
        LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
        User user = userMapper.selectOne(userLambdaQueryWrapper.eq(User::getUsername, userDetail.getUsername()));
        if (user !=null){
            throw new BusinessException("该用户名已存在");
        }
        UserExtend userExtend = userDetail.getUserExtend();
        //2个表必定要更新一个，检查一个不为空即可，另一个去数据库检验把，也为空就抛异常
        if (userExtend.UpdateIsNotEmpty()){
            userExtendMapper.updateById(userExtend);
        }
        userMapper.updateById(userDetail.getUser());
        return ResponseResult.success("更新成功！");
    }

    /**
     *
     * @param likeDto 考虑到本业务并没有点赞列表的显示需求，目前只用hash存储关系
     * @return
     */
    @Override
    public ResponseResult changeLike(LikeDto likeDto) {
//        1.先查询redis中对应hash有无点赞信息，如果没有说明是第一次点赞，增加记录到redis，如果有，更改状态（lua）
//        2.更改redis对应的计数器（lua）
        List list = new ArrayList<>();
//        --  KEY[1]代表点赞关系的key,KEY[2]该实体得到的赞数量的key,KEY[3]该实体的创建者得到的赞数量的key
        list.add(RedisConstant.getLikeRelationKey(likeDto.getEntityId().toString(),likeDto.getEntityType().toString(),likeDto.getEntityUserId().toString()));
        list.add(RedisConstant.getEntityLikeCountKey(likeDto.getEntityId().toString(),likeDto.getEntityType().toString()));
        list.add(RedisConstant.getEntityLikeCountKey(likeDto.getEntityUserId().toString(),String.valueOf(Constants.USER_TYPE)));
        Long execute = (Long)redisTemplate.execute(LIKE_SCRIPT, list, new ArrayList<>());
        if (execute.intValue() == RedisConstant.RESULT_FIRST_SAVE){
            // 3.如果是第一次点赞，还需要将点赞通知放入对应mq
            rabbitTemplate.convertAndSend(MqConstants.EXCHANGE_OPTION_KEY,MqConstants.OPTION_LIKE,likeDto);
        }else if(execute.intValue() == RedisConstant.RESULT_ERROR){
            throw new BusinessException(ResultEnum.SYSTEM_ERROR);
        }
        return ResponseResult.success();

    }

    /**
     * 维护zset粉丝列表和关注列表，zset其他实体类型关注列表，hash关注关系总表
     * @param focus
     * @return
     */
    @Override
    public ResponseResult changeFocus(Focus focus) {
        List list = new ArrayList<>();
        long currentTimeMillis = System.currentTimeMillis();
        //关注的是用户还是其他类型比如话题，处理方式也不一样
        if (focus.getEntityType() == Constants.USER){
            //       KEYS[1]代表关注关系的key 往被关注用户的粉丝队列插入粉丝id，KEYS[2]为用户列表名，KEYS[3]为分数，KEYS[4]为存入的值
            list.add(RedisConstant.getFocusRelationKey(String.valueOf(focus.getEntityId()),String.valueOf(focus.getEntityType()),String.valueOf(focus.getUserId())));
            list.add(RedisConstant.getFanUserKey(String.valueOf(focus.getEntityId())));
            list.add(String.valueOf(currentTimeMillis));
            list.add(String.valueOf(focus.getUserId()));
//        往点击关注的用户的关注队列插入关注者id，KEYS[5]为用户列表名，KEYS[6]为分数，KEYS[7]为存入的值
            list.add(RedisConstant.getFocusUserKey(String.valueOf(focus.getUserId()),String.valueOf(focus.getEntityType())));
            list.add(String.valueOf(currentTimeMillis));
            list.add(String.valueOf(focus.getEntityId()));
        }else{
            list.add(RedisConstant.getFocusRelationKey(String.valueOf(focus.getEntityId()),String.valueOf(focus.getEntityType()),String.valueOf(focus.getUserId())));
            //        往点击关注的用户的关注队列插入关注的实体id，KEYS[5]为列表名，KEYS[6]为分数，KEYS[7]为存入的值
            list.add(RedisConstant.getFocusUserKey(String.valueOf(focus.getUserId()),String.valueOf(focus.getEntityType())));
            list.add(String.valueOf(currentTimeMillis));
            list.add(String.valueOf(focus.getEntityId()));

        }
        Long execute = (Long)redisTemplate.execute(FOCUS_SCRIPT, list, focus.getEntityType());
        //关注业务暂时不需要通知
        if(execute.intValue() == RedisConstant.RESULT_ERROR){
            throw new BusinessException(ResultEnum.SYSTEM_ERROR);
        }
        return ResponseResult.success();
    }

    /**
     * 维护hash收藏总表，每个用户维护一个zset收藏列表
     * @param collect
     * @return
     */
    @Override
    public ResponseResult changeCollect(Collect collect) {
        List list = new ArrayList<>();
        long currentTimeMillis = System.currentTimeMillis();
        list.add(RedisConstant.getCollectRelationKey(String.valueOf(collect.getEntityId()),String.valueOf(collect.getEntityType()),String.valueOf(collect.getUserId())));
        list.add(RedisConstant.getCollectKey(String.valueOf(collect.getEntityId()),String.valueOf(collect.getEntityType()),String.valueOf(collect.getUserId())));
        list.add(String.valueOf(currentTimeMillis));
        list.add(String.valueOf(collect.getEntityId()));
        Long execute = (Long)redisTemplate.execute(COLLECT_SCRIPT, list, new ArrayList<>());
        if(execute.intValue() == RedisConstant.RESULT_ERROR){
            throw new BusinessException(ResultEnum.SYSTEM_ERROR);
        }
        return ResponseResult.success();
    }


}




